﻿#include "ObjectSelector.h"
#include "LumDebug.h"

using BaseClass = QObject;

#include <QApplication>
#include <QWidget>
#include <QTimer>
#include <QSet>

#include "ObjectRubberBand.h"

struct ObjectSelector::ObjectSelectorData {
    QPointer<QObject> current;
    QSet<QPointer<QObject>> selected;
    QPointer<ObjectRubberBand> rubberBand;
    QSet<QPointer<QObject>> excluded;
};

size_t qHash(const QPointer<QObject> &object, size_t seed = 0) {
    Q_UNUSED(seed);
    return qHash(object.data());
}

ObjectSelector::~ObjectSelector() { }

ObjectSelector::ObjectSelector(QObject* parent) : BaseClass{parent} {
    d.reset(new ObjectSelectorData);

    connect(this, SIGNAL(currentChanged(QPointer<QObject>,QPointer<QObject>)), this, SLOT(onCurrentChanged(QPointer<QObject>,QPointer<QObject>)));
    connect(this, SIGNAL(selectionChanged(QList<QPointer<QObject>>,QList<QPointer<QObject>>)), this, SLOT(onSelectionChanged(QList<QPointer<QObject>>,QList<QPointer<QObject>>)));

}

QPointer<ObjectSelector> ObjectSelector::instance() {
    static QPointer<ObjectSelector> singleton;
    if(!singleton) {
        singleton = new ObjectSelector;
    }
    return singleton;
}

void ObjectSelector::excludeObject(QPointer<QObject> object) {
    d->excluded.insert(object);
}

bool isAncesstor(QPointer<QObject> parent, QPointer<QObject> child) {
    //const auto &parentName = parent->objectName();
    //const auto &childName = child->objectName();
    if(parent == child) {
        return true;
    }
    for(const auto item : parent->children()) {
        if(isAncesstor(item, child)) {
            return true;
        }
    }
    return false;
}

void ObjectSelector::select(QPointer<QObject> object, ObjectSelectFlags command) {
    for(const auto item : d->excluded) {
        if(isAncesstor(item, object)) {
            return ;
        }
    }

    // 过滤无效命令
    const QSet<ObjectSelectFlags> validate =  {
        Clear, Select, Deselect, Toggle, Current, SelectCurrent,
        ToggleCurrent, ClearAndSelect
    };
    if(!validate.contains(command)) {
        return ;
    }

    // 更新当前对象
    if(command.testFlag(Current)) {
        setCurrentObject(object);
    }

    // 更新选中对象
    auto &after = d->selected;
    auto before = after;

    if(command.testFlag(Clear)) {
        after.clear();
    }

    if(command.testFlag(Toggle)) {
        if(after.contains(object)) {
            after.remove(object);
        } else {
            after.insert(object);
        }
    }

    if(command.testFlag(Deselect)) {
        after.remove(object);
    }

    if(command.testFlag(Select)) {
        after.insert(object);
    }

    if(after == before) {
        return ;
    }

    const auto &selected = after.subtract(before);
    const auto &deselected = before.subtract(after);
    emit selectionChanged(selected.toList(), deselected.toList());
}

void ObjectSelector::clearCurrentObject() {
    setCurrentObject(nullptr);
}

QPointer<QObject> ObjectSelector::currentObject() const {
    return d->current;
}

void ObjectSelector::setEnabled(bool enable) {
    if(enable) {
        qApp->installEventFilter(this);
    } else {
        qApp->removeEventFilter(this);
    }
}

bool ObjectSelector::eventFilter(QObject* watched, QEvent* event) {
    const auto &result = BaseClass::eventFilter(watched, event);
    const auto &type = event->type();

    if(watched == qApp) {
        return result;
    }

    else if(watched->isWidgetType()) {
        const auto widget = qobject_cast<QWidget*>(watched);
        if(widget) {
            switch(type) {
            case QEvent::Enter: {
                    select(widget, Current);
                } break;
            case QEvent::Leave: {
                    clearCurrentObject();
                } break;
            default: break;
            }
        }
    }

    return result;
}

void ObjectSelector::setCurrentObject(QPointer<QObject> object) {
    if(d->current == object) {
        return ;
    }

    const auto previous = d->current;
    d->current = object;
    emit currentChanged(d->current, previous);
}

void ObjectSelector::onCurrentChanged(QPointer<QObject> current, const QPointer<QObject> previous) {
    if(!current || !current->isWidgetType()) {
        return ;
    }

    const auto widget = qobject_cast<QWidget*>(current);
    if(!widget) {
        return ;
    }

    auto &band = d->rubberBand;
    if(!band) {
        band = new ObjectRubberBand;
    }

    band->setParent(widget);
    band->setWindowFlag(Qt::Tool);
    band->show();
}

void ObjectSelector::onSelectionChanged(const QList<QPointer<QObject> > selected, const QList<QPointer<QObject> > deselected) {
    //DEBUG;
}




